/**
* Oxide
*
* Copyright (c) 2011 Dinosaur Kick Pty Ltd, All rights reserved.
*
* This source code is made available under the "Modified BSD" licensing arrangement.
* See license.txt for details.
*/
// ----------------------------------------------------------------------------
#ifndef O2_BIDIRECTIONAL_ITERATOR_ADAPTOR_INC
#define O2_BIDIRECTIONAL_ITERATOR_ADAPTOR_INC
// ----------------------------------------------------------------------------
#include <list>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/make_shared.hpp>
// ----------------------------------------------------------------------------
namespace o2 {
// ----------------------------------------------------------------------------

template <typename IteratorT, typename ContainerT = std::list<typename std::iterator_traits<IteratorT>::value_type> >
class bidirectional_iterator_adaptor
	: public boost::iterator_facade
		<
			bidirectional_iterator_adaptor<IteratorT, ContainerT>,
			typename std::iterator_traits<IteratorT>::value_type,
			boost::bidirectional_traversal_tag,
			typename std::iterator_traits<IteratorT>::reference,
			typename std::iterator_traits<IteratorT>::difference_type
		>
{
	public:
		typedef IteratorT iterator_type;
		typedef typename std::iterator_traits<IteratorT>::reference reference;

		explicit bidirectional_iterator_adaptor(const iterator_type& iter)
			: m_iter(iter)
			{ }

		bidirectional_iterator_adaptor(const bidirectional_iterator_adaptor& iter)
			: m_container(iter.m_container)
			, m_backtrack_iter(iter.m_backtrack_iter)
			, m_iter(iter.m_iter)
			{ }

		void clear(void)
		{
			if (m_container)
			{
				m_backtrack_iter = m_container->begin();
			}
		}

	private:
	    friend class boost::iterator_core_access;
		typedef ContainerT container_type; 

		void increment(void)
		{
			if (m_container && m_backtrack_iter != m_container->end())
			{
				++m_backtrack_iter;
			}
			else
			{
				real_increment();
			}
		}

		void decrement(void)
		{
			--m_backtrack_iter;
		}

		void real_increment(void)
		{
			prepare_container();
			m_container->push_back(*m_iter);
			m_backtrack_iter = m_container->end();
			++m_iter;
		}

		bool equal(const bidirectional_iterator_adaptor& iter) const
		{
			return m_iter == iter.m_iter
				&& backtrack_distance() == iter.backtrack_distance();
		}

		typename ContainerT::difference_type backtrack_distance(void) const
		{
			return m_container
				? std::distance<typename ContainerT::const_iterator>(m_backtrack_iter, m_container->end())
				: 0;
		}

		reference dereference(void) const
		{
			return m_container && m_backtrack_iter != m_container->end()
				? *m_backtrack_iter
				: *m_iter;
		}

		void prepare_container(void)
		{
			if (!m_container)
			{
				m_container = boost::make_shared<container_type>();
			}
			else if (m_container.use_count() > 1)
			{
				m_container = boost::make_shared<container_type>(*m_container);
			}
		}

		boost::shared_ptr<container_type> m_container;
		typename container_type::iterator m_backtrack_iter;
		iterator_type m_iter;
};

// ----------------------------------------------------------------------------
} // namespace o2
#endif // O2_BIDIRECTIONAL_ITERATOR_ADAPTOR_INC
// ----------------------------------------------------------------------------
